package com.googlecode.objectify.hbase;

import com.google.appengine.api.datastore.Transaction;
import fi.aalto.hacid.HAcidClient;
import fi.aalto.hacid.HAcidTxn;
import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import lombok.Delegate;
import lombok.Getter;
import org.apache.hadoop.hbase.util.Base64;

/**
 *
 * @author mjaggard
 */
public class HBaseTransaction implements Transaction
{

	private interface Commit
	{

		void commit();
	}
	private final String id;
	private final String app;
	private final ExecutorService exec;
	@Delegate(excludes = Commit.class)
	private HAcidTxn txn;
	//TODO: Instead of making this final, maybe we need to have a count of
	//transaction users and only allow a commit when they've all finished with it?
	@Getter
	private final boolean autocommit;

	public HBaseTransaction(String app, ExecutorService exec, HAcidClient client)
	{
		this(app, exec, client, false);
	}

	public HBaseTransaction(String app, ExecutorService exec, HAcidClient client, boolean autocommit)
	{
		byte[] bytes = new byte[100];
		Random random = new Random();
		random.nextBytes(bytes);
		id = Base64.encodeBytes(bytes);
		this.app = app;
		this.exec = exec;
		this.txn = new HAcidTxn(client);
		this.autocommit = autocommit;
	}

	@Override
	public String getId()
	{
		return id;
	}

	@Override
	public String getApp()
	{
		return app;
	}

	@Override
	public Future<Void> commitAsync()
	{
		return exec.submit(new Callable<Void>()
		{
			@Override
			public Void call() throws Exception
			{
				commit();
				return null;
			}
		});
	}

	@Override
	public Future<Void> rollbackAsync()
	{
		return exec.submit(new Callable<Void>()
		{
			@Override
			public Void call() throws Exception
			{
				rollback();
				return null;
			}
		});
	}

	@Override
	public void commit()
	{
		try
		{
			if (!txn.commit(2))
			{
				throw new ConcurrentModificationException(); //TODO: Is this correct?
			}
			else
			{
				txn = null;
			}
		}
		catch (IOException ex)
		{
			throw new ConcurrentModificationException(); //TODO: Is this correct?
		}
	}

	@Override
	public boolean isActive()
	{
		return txn != null;
	}

	@Override
	public void rollback()
	{
		txn = null; //TODO: Is this correct?
	}
}
